Analysis of Lung Cancer Data using CART Regression

Data Overview

Lung cancer is a serious disease that can lead to other health conditions and death.The purpose of this dataset is to find potential predictors of lung cancer. By identifying these risk factors, doctors and patients can help identify high risk patients and catch cancer early on. The response variable to be used in this analysis is, Lung Cancer.

Predictor variables featured in this analysis are:

  • Gender - Indicated as M for male and F for female
  • Age Age of patient

The remaining predictor variables are factored as 1 for NO and 2 for YES. These variables are Coughing,Shortness of Breath, Smoking, Yellow Fingers, Anxiety, Peer Pressure, Chronic Disease, Fatigue, Allergy, Wheezing, Alcohol Consuming, Swallowing Difficulty, and Chest Pain. The data was obtained using Kaggle. It has 16 variable columns and 309 observations.

Abstract

This analysis will use CART classification to find significant predictors of the target variable, lung cancer. The CART classification analysis found that the key factors in predicting lung cancer were difficulty swallowing, yellow fingers, wheezing, alcohol consumption, and allergy. However, for this analysis logistic regression performed better than the two CART classification methods. Meaning for this analysis, it would have been more efficient to run logistic regression to find the best fit model to predict lung cancer. Based on the findings, from CART classification difficulty swallowing is key predictor in determining lung cancer and individuals who experience this symptom, should have regular screenings with their doctor. As well, other key predictors are allergy, alcohol consumption, and wheezing. These symptoms should also be discussed with an individual’s doctor and may be a precursor to or sign that an individual has lung cancer.

Introduction

This analysis seeks to answer the question of ‘What key factors can predict lung cancer?’ This analysis can help individuals obtain early treatment for lung cancer or allow an individual to take preventative steps against lung cancer. Some limitations of the analysis are most variables have been answered with a “yes” or “no”, which does not give us the complete story. For instance, smoking is answered with a “yes” or “no” which does not let us analyze how smoking over a period of time can lead to lung cancer.

data <- read.csv('https://raw.githubusercontent.com/GabbyK8/Datasets/refs/heads/main/survey%20lung%20cancer.csv')

Exploratory Data Analysis

a <- ggplot(data, aes(x = SWALLOWING.DIFFICULTY, fill= LUNG_CANCER )) +
  geom_bar(position = "dodge") +
  labs(
    x = "Difficulty Swallowing",
    y = "Proportion",
    fill = "Lung Cancer",
    title = "Prevelance of Lung Cancer Related to Difficulty Swallowing"
  ) +
  theme_minimal()
ggplotly(a)

This figure shows the relationship between difficulty swallowing and lung cancer. Based on the graph, there are more people with lung cancer that smoke than for those who do not smoke. Meaning there is a possible relationship between difficulty swallowing and being diagnosed with lung cancer.

ggplot(data, aes(x = LUNG_CANCER, y = AGE)) +
  geom_boxplot(fill = "skyblue") +
  labs(x = "Category", y = "Value", title = "Lung Cancer by Age")
The boxplot shows the relationship between age and lung cancer. It shows that most people who are diagnosed with lung cancer are older. Suggesting a correlation between age and lung cancer diagnosis.

The boxplot shows the relationship between age and lung cancer. It shows that most people who are diagnosed with lung cancer are older. Suggesting a correlation between age and lung cancer diagnosis.

CART Classification

The outcome variable is a binary variable, so this analysis will rely on CART Classification to help predict the categorical target variable.

# Split data into training (70%) and test (30%) sets
set.seed(123)
train.index <- createDataPartition(data$LUNG_CANCER, p = 0.7, list = FALSE)
train.data <- data[train.index, ]
test.data <- data[-train.index, ]

# Build the initial classification tree
tree.model <- rpart(LUNG_CANCER ~ ., 
                    data = train.data,
                    method = "class",   # classification tree
                    parms = list(split = "gini",  # Using Gini index
                                 #FNcost = 1, FPcost = 0.5,
                                 loss = matrix(c(0, 0.5, 1, 0), nrow = 2)  
                                 ),
                    control = rpart.control(minsplit = 15,  # Min 15 obs to split
                                           minbucket = 5,   # Min 7 obs in leaf
                                           # Complexity parameter
                                           cp = 0.001, # complex parameter
                                           maxdepth = 5))# Max tree depth
rpart.plot(tree.model, 
           extra = 104, # check the help document for more information
           # color palette is a sequential color scheme that blends green (G) to blue (Bu)
           box.palette = "GnBu",  
           branch.lty = 1, 
           shadow.col = "gray", 
           nn = TRUE)
The tree classification shows the first split at swallowing difficulty, indicating it was the most significant variable of the split. Overall, it shows that individuals with difficulty swallowing and allergy have a high chance of getting lung cancer. It also suggests that even without allergy, wheezing, alcohol consumption, and yellow fingers can raise probability of getting diagnosed with lung cancer.

The tree classification shows the first split at swallowing difficulty, indicating it was the most significant variable of the split. Overall, it shows that individuals with difficulty swallowing and allergy have a high chance of getting lung cancer. It also suggests that even without allergy, wheezing, alcohol consumption, and yellow fingers can raise probability of getting diagnosed with lung cancer.

# Print the complexity parameter table
pander(tree.model$cptable)
CP nsplit rel error xerror xstd
0.125 0 1 0.5 0.08818
0.08929 3 0.625 0.8929 0.1499
0.05357 4 0.5357 0.7321 0.1375
0.001 5 0.4821 0.6786 0.1328

The table above is a complexity parameter table from the decision tree above. At the beginning of the tree there are zero splits with a training error of 0, which is our baseline. As the tree grows there are more splits and a decreased relative error, suggesting a better fit on training data. Ideally, we want the smallest subtree where xerror is within 1 SE of the minimum xerror. The minimum xerror is 0.6786, shown in the last row. Using the 1 SE rule, our threshold would be the minimum xerror plus it’s standard deviation which would be 0.8114. The first row to meet this rule is the third row which has 4 splits and a CP of 0.05357, because it’s xerror is 0.7321 which is less than 0.8114.This means the tree should be pruned to 4 splits for model simplicity and accuracy.

# Plot the cross-validation results
plotcp(tree.model)
The plot above shows the corss-validarion error (rel error) vs the complexity parameter (cp) from the decision tree model. The horrizontal dashed line shows the minimum cross-validation error + 1SE. The 1-SE rule suggests choosing the simplest model within 1 standard error of the lowest cross-validation error. The dashed line crosses the error bar of the tree size 1, meaning this is the simplest model that will still perform adequately. Our optimal cp would be 0.0073, which is at 6 splits.

The plot above shows the corss-validarion error (rel error) vs the complexity parameter (cp) from the decision tree model. The horrizontal dashed line shows the minimum cross-validation error + 1SE. The 1-SE rule suggests choosing the simplest model within 1 standard error of the lowest cross-validation error. The dashed line crosses the error bar of the tree size 1, meaning this is the simplest model that will still perform adequately. Our optimal cp would be 0.0073, which is at 6 splits.

Pruning

# Find the optimal cp value that minimizes cross-validated error
min.cp <- tree.model$cptable[which.min(tree.model$cptable[,"xerror"]),"CP"]

# Prune the tree using the optimal cp
pruned.tree.1SE <- prune(tree.model, cp = 0.0073)  
pruned.tree.min <- prune(tree.model, cp = min.cp)

# Visualize the pruned tree
rpart.plot(pruned.tree.1SE, 
           extra = 104, # check the help document for more information
           # color palette is a sequential color scheme that blends green (G) to blue (Bu)
           box.palette = "GnBu",  
           branch.lty = 1, 
           shadow.col = "gray", 
           nn = TRUE)
The above model shows the tree after pruning. This result is the same as our first model, since the optimal cp is as 6 splits.

The above model shows the tree after pruning. This result is the same as our first model, since the optimal cp is as 6 splits.

# Visualize the pruned tree
rpart.plot(pruned.tree.min, 
           extra = 104, # check the help document for more information
           # color palette is a sequential color scheme that blends green (G) to blue (Bu)
           box.palette = "GnBu",  
           branch.lty = 1, 
           shadow.col = "gray", 
           nn = TRUE)
This is the final pruned tree based on the plot of cross-validation error vs CP. This shows that swallowing difficulty is significant alone in predicting lung cancer.

This is the final pruned tree based on the plot of cross-validation error vs CP. This shows that swallowing difficulty is significant alone in predicting lung cancer.

# Make predictions on the test set
test.data$LUNG_CANCER<- as.factor(test.data$LUNG_CANCER)
train.data$LUNG_CANCER<- as.factor(train.data$LUNG_CANCER)

pred.label.1SE <- predict(pruned.tree.1SE, test.data, type = "class") # default cutoff 0.5
pred.prob.1SE <- predict(pruned.tree.1SE, test.data, type = "prob")[,2]
##
pred.label.min <- predict(pruned.tree.min, test.data, type = "class") # default cutoff 0.5
pred.prob.min <- predict(pruned.tree.min, test.data, type = "prob")[,2]

# Confusion matrix
#conf.matrix <- confusionMatrix(pred.label, test.data$diabetes, positive = "pos")
#print(conf.matrix)

########################
###  logistic regression
logit.fit <- glm(LUNG_CANCER ~ ., data = train.data, family = binomial)
AIC.logit <- step(logit.fit, direction = "both", trace = 0)
pred.logit <- predict(AIC.logit, test.data, type = "response")

# ROC curve and AUC
roc.tree.1SE <- roc(test.data$LUNG_CANCER, pred.prob.1SE)
roc.tree.min <- roc(test.data$LUNG_CANCER, pred.prob.min)
roc.logit <- roc(test.data$LUNG_CANCER, pred.logit)

##
### Sen-Spe
tree.1SE.sen <- roc.tree.1SE$sensitivities
tree.1SE.spe <- roc.tree.1SE$specificities
#
tree.min.sen <- roc.tree.min$sensitivities
tree.min.spe <- roc.tree.min$specificities
#
logit.sen <- roc.logit$sensitivities
logit.spe <- roc.logit$specificities
## AUC
auc.tree.1SE <- roc.tree.1SE$auc
auc.tree.min <- roc.tree.min$auc
auc.logit <- roc.logit$auc
###
plot(1-logit.spe, logit.sen,  
     xlab = "1 - specificity",
     ylab = "sensitivity",
     col = "darkred",
     type = "l",
     lty = 1,
     lwd = 1,
     main = "ROC: CART and Logistic Regression")
lines(1-tree.1SE.spe, tree.1SE.sen, 
      col = "blue",
      lty = 1,
      lwd = 1)
lines(1-tree.min.spe, tree.min.sen,      
      col = "orange",
      lty = 1,
      lwd = 1)
abline(0,1, col = "skyblue3", lty = 2, lwd = 2)
legend("bottomright", c("Logistic", "Tree 1SE", "Tree Min"),
       lty = c(1,1,1), lwd = rep(1,3),
       col = c("red", "blue", "orange"),
       bty="n",cex = 0.8)
## annotation - AUC
text(0.8, 0.46, paste("Logistic AUC: ", round(auc.logit,4)), cex = 0.8)
text(0.8, 0.4, paste("Tree 1SE AUC: ", round(auc.tree.1SE,4)), cex = 0.8)
text(0.8, 0.34, paste("Tree Min AUC: ", round(auc.tree.min,4)), cex = 0.8)
The plot compares logistic regression and the pruned Tree models. Based on the AUC and ROC, logistic regression performs better than the other two models.

The plot compares logistic regression and the pruned Tree models. Based on the AUC and ROC, logistic regression performs better than the other two models.

Optimal Cutoff

# preditive probabilities of tree.min model.
pred.prob.min <- predict(pruned.tree.min, train.data, type = "prob")[,2]
##
cost <- NULL
cutoff <-seq(0,1, length = 10)
##
for (i in 1:10){
  pred.label <- ifelse(pred.prob.min > cutoff[i], "YES", "NO")
  FN <- sum(pred.label == "NO" & train.data$LUNG_CANCER == "YES")
  FP <- sum(pred.label == "YES" & train.data$LUNG_CANCER == "NO")
  cost[i] = 5*FP + 20*FN
}
## identify optimal cut-off
min.ID <- which(cost == min(cost))   # could have multiple minimum
optim.prob <- mean(cutoff[min.ID])   # take the average of the cut-offs
##
plot(cutoff, cost, type = "b", col = "navy",
     main = "Cutoff vs Misclassification Cost")
##
text(0.2, 3500, paste("Optimal cutoff:", round(optim.prob,4)), cex = 0.8)
The above plot shows the optimal cutoff to be 0.3889. This threshold is used to minimize missclassification.

The above plot shows the optimal cutoff to be 0.3889. This threshold is used to minimize missclassification.

Conclusion

The logistic regression model performs superior to the suggested CART Classification models. However, the CART models showed that difficulty breathing was a key predictor in lung cancer. Other key predictors were alcohol consumption, wheezing, and allergy.

LS0tCnRpdGxlOiAiUHJvamVjdCAzIgphdXRob3I6ICJHYWJyaWVsbGEgS2hhbGlsIgpkYXRlOiAiMjAyNS0wNC0yOSIKb3V0cHV0OgoKICBodG1sX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHdvcmRfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAga2VlcF9tZDogeWVzCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIGZpZ193aWR0aDogMwogICAgZmlnX2hlaWdodDogMwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyMnB4OwogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMThweDsKICBmb250LXdlaWdodDogYm9sZDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBuYXZ5OwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwogIGZvbnQtd2VpZ2h0OiBib2xkOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogY2VudGVyOwogICAgZm9udC13ZWlnaHQ6IGJvbGQ7Cn0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLwogICAgZm9udC1zaXplOiAyMHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7CiAgICBmb250LXdlaWdodDogYm9sZDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgMyBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cgpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQoKLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0KCnAgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9Cgo8L3N0eWxlPgpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQogICBsaWJyYXJ5KGtuaXRyKQp9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsKICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKbGlicmFyeSh0aWR5dmVyc2UpCn0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgewogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQpsaWJyYXJ5KEdHYWxseSkKfQoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KHBhbmRlcikKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShycGFydCkgICAgICAgICMgRm9yIGRlY2lzaW9uIHRyZWVzCmxpYnJhcnkocnBhcnQucGxvdCkgICAjIEZvciB2aXN1YWxpemluZyB0cmVlcwpsaWJyYXJ5KGNhcmV0KSAgICAgICAgIyBGb3IgbW9kZWwgZXZhbHVhdGlvbgpsaWJyYXJ5KHBST0MpICAgICAgICAgIyBGb3IgUk9DIGFuYWx5c2lzCgprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUsICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAKCmBgYAojIEFuYWx5c2lzIG9mIEx1bmcgQ2FuY2VyIERhdGEgdXNpbmcgQ0FSVCBSZWdyZXNzaW9uCgojIyBEYXRhIE92ZXJ2aWV3CgpMdW5nIGNhbmNlciBpcyBhIHNlcmlvdXMgZGlzZWFzZSB0aGF0IGNhbiBsZWFkIHRvIG90aGVyIGhlYWx0aCBjb25kaXRpb25zIGFuZCBkZWF0aC5UaGUgcHVycG9zZSBvZiB0aGlzIGRhdGFzZXQgaXMgdG8gZmluZCBwb3RlbnRpYWwgcHJlZGljdG9ycyBvZiBsdW5nIGNhbmNlci4gQnkgaWRlbnRpZnlpbmcgdGhlc2UgcmlzayBmYWN0b3JzLCBkb2N0b3JzIGFuZCBwYXRpZW50cyBjYW4gaGVscCBpZGVudGlmeSBoaWdoIHJpc2sgcGF0aWVudHMgYW5kIGNhdGNoIGNhbmNlciBlYXJseSBvbi4gVGhlIHJlc3BvbnNlIHZhcmlhYmxlIHRvIGJlIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBpcywgKipMdW5nIENhbmNlcioqLgoKUHJlZGljdG9yIHZhcmlhYmxlcyBmZWF0dXJlZCBpbiB0aGlzIGFuYWx5c2lzIGFyZToKCi0gICAqKkdlbmRlcioqIC0gSW5kaWNhdGVkIGFzIE0gZm9yIG1hbGUgYW5kIEYgZm9yIGZlbWFsZQotICAgKipBZ2UqKiBBZ2Ugb2YgcGF0aWVudAoKVGhlIHJlbWFpbmluZyBwcmVkaWN0b3IgdmFyaWFibGVzIGFyZSBmYWN0b3JlZCBhcyAxIGZvciBOTyBhbmQgMiBmb3IgWUVTLiBUaGVzZSB2YXJpYWJsZXMgYXJlICoqQ291Z2hpbmcqKiwqKlNob3J0bmVzcyBvZiBCcmVhdGgqKiwgKipTbW9raW5nKiosICoqWWVsbG93IEZpbmdlcnMqKiwgKipBbnhpZXR5KiosICoqUGVlciBQcmVzc3VyZSoqLCAqKkNocm9uaWMgRGlzZWFzZSoqLCAqKkZhdGlndWUqKiwgKipBbGxlcmd5KiosICoqV2hlZXppbmcqKiwgKipBbGNvaG9sIENvbnN1bWluZyoqLCAqKlN3YWxsb3dpbmcgRGlmZmljdWx0eSoqLCBhbmQgKipDaGVzdCBQYWluKiouClRoZSBkYXRhIHdhcyBvYnRhaW5lZCB1c2luZyBLYWdnbGUuIEl0IGhhcyAxNiB2YXJpYWJsZSBjb2x1bW5zIGFuZCAzMDkgb2JzZXJ2YXRpb25zLgoKIyMgQWJzdHJhY3QKClRoaXMgYW5hbHlzaXMgd2lsbCB1c2UgQ0FSVCBjbGFzc2lmaWNhdGlvbiB0byBmaW5kIHNpZ25pZmljYW50IHByZWRpY3RvcnMgb2YgdGhlIHRhcmdldCB2YXJpYWJsZSwgbHVuZyBjYW5jZXIuIFRoZSBDQVJUIGNsYXNzaWZpY2F0aW9uIGFuYWx5c2lzIGZvdW5kIHRoYXQgdGhlIGtleSBmYWN0b3JzIGluIHByZWRpY3RpbmcgbHVuZyBjYW5jZXIgd2VyZSBkaWZmaWN1bHR5IHN3YWxsb3dpbmcsIHllbGxvdyBmaW5nZXJzLCB3aGVlemluZywgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIGFsbGVyZ3kuIEhvd2V2ZXIsIGZvciB0aGlzIGFuYWx5c2lzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcGVyZm9ybWVkIGJldHRlciB0aGFuIHRoZSB0d28gQ0FSVCBjbGFzc2lmaWNhdGlvbiBtZXRob2RzLiBNZWFuaW5nIGZvciB0aGlzIGFuYWx5c2lzLCBpdCB3b3VsZCBoYXZlIGJlZW4gbW9yZSBlZmZpY2llbnQgdG8gcnVuIGxvZ2lzdGljIHJlZ3Jlc3Npb24gdG8gZmluZCB0aGUgYmVzdCBmaXQgbW9kZWwgdG8gcHJlZGljdCBsdW5nIGNhbmNlci4gQmFzZWQgb24gdGhlIGZpbmRpbmdzLCBmcm9tIENBUlQgY2xhc3NpZmljYXRpb24gZGlmZmljdWx0eSBzd2FsbG93aW5nIGlzIGtleSBwcmVkaWN0b3IgaW4gZGV0ZXJtaW5pbmcgbHVuZyBjYW5jZXIgYW5kIGluZGl2aWR1YWxzIHdobyBleHBlcmllbmNlIHRoaXMgc3ltcHRvbSwgc2hvdWxkIGhhdmUgcmVndWxhciBzY3JlZW5pbmdzIHdpdGggdGhlaXIgZG9jdG9yLiBBcyB3ZWxsLCBvdGhlciBrZXkgcHJlZGljdG9ycyBhcmUgYWxsZXJneSwgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIHdoZWV6aW5nLiBUaGVzZSBzeW1wdG9tcyBzaG91bGQgYWxzbyBiZSBkaXNjdXNzZWQgd2l0aCBhbiBpbmRpdmlkdWFsJ3MgZG9jdG9yIGFuZCBtYXkgYmUgYSBwcmVjdXJzb3IgdG8gb3Igc2lnbiB0aGF0IGFuIGluZGl2aWR1YWwgaGFzIGx1bmcgY2FuY2VyLgoKIyMgSW50cm9kdWN0aW9uCgpUaGlzIGFuYWx5c2lzIHNlZWtzIHRvIGFuc3dlciB0aGUgcXVlc3Rpb24gb2YgJ1doYXQga2V5IGZhY3RvcnMgY2FuIHByZWRpY3QgbHVuZyBjYW5jZXI/JyBUaGlzIGFuYWx5c2lzIGNhbiBoZWxwIGluZGl2aWR1YWxzIG9idGFpbiBlYXJseSB0cmVhdG1lbnQgZm9yIGx1bmcgY2FuY2VyIG9yIGFsbG93IGFuIGluZGl2aWR1YWwgdG8gdGFrZSBwcmV2ZW50YXRpdmUgc3RlcHMgYWdhaW5zdCBsdW5nIGNhbmNlci4gU29tZSBsaW1pdGF0aW9ucyBvZiB0aGUgYW5hbHlzaXMgYXJlIG1vc3QgdmFyaWFibGVzIGhhdmUgYmVlbiBhbnN3ZXJlZCB3aXRoIGEgInllcyIgb3IgIm5vIiwgd2hpY2ggZG9lcyBub3QgZ2l2ZSB1cyB0aGUgY29tcGxldGUgc3RvcnkuIEZvciBpbnN0YW5jZSwgc21va2luZyBpcyBhbnN3ZXJlZCB3aXRoIGEgInllcyIgb3IgIm5vIiB3aGljaCBkb2VzIG5vdCBsZXQgdXMgYW5hbHl6ZSBob3cgc21va2luZyBvdmVyIGEgcGVyaW9kIG9mIHRpbWUgY2FuIGxlYWQgdG8gbHVuZyBjYW5jZXIuCgpgYGB7cn0KZGF0YSA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0dhYmJ5SzgvRGF0YXNldHMvcmVmcy9oZWFkcy9tYWluL3N1cnZleSUyMGx1bmclMjBjYW5jZXIuY3N2JykKCgpgYGAKCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBmaWd1cmUgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGRpZmZpY3VsdHkgc3dhbGxvd2luZyBhbmQgbHVuZyBjYW5jZXIuIEJhc2VkIG9uIHRoZSBncmFwaCwgdGhlcmUgYXJlIG1vcmUgcGVvcGxlIHdpdGggbHVuZyBjYW5jZXIgdGhhdCBzbW9rZSB0aGFuIGZvciB0aG9zZSB3aG8gZG8gbm90IHNtb2tlLiBNZWFuaW5nIHRoZXJlIGlzIGEgcG9zc2libGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlmZmljdWx0eSBzd2FsbG93aW5nIGFuZCBiZWluZyBkaWFnbm9zZWQgd2l0aCBsdW5nIGNhbmNlci4nIH0KCmEgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gU1dBTExPV0lORy5ESUZGSUNVTFRZLCBmaWxsPSBMVU5HX0NBTkNFUiApKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgbGFicygKICAgIHggPSAiRGlmZmljdWx0eSBTd2FsbG93aW5nIiwKICAgIHkgPSAiUHJvcG9ydGlvbiIsCiAgICBmaWxsID0gIkx1bmcgQ2FuY2VyIiwKICAgIHRpdGxlID0gIlByZXZlbGFuY2Ugb2YgTHVuZyBDYW5jZXIgUmVsYXRlZCB0byBEaWZmaWN1bHR5IFN3YWxsb3dpbmciCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmdncGxvdGx5KGEpCmBgYAoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYm94cGxvdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWdlIGFuZCBsdW5nIGNhbmNlci4gSXQgc2hvd3MgdGhhdCBtb3N0IHBlb3BsZSB3aG8gYXJlIGRpYWdub3NlZCB3aXRoIGx1bmcgY2FuY2VyIGFyZSBvbGRlci4gU3VnZ2VzdGluZyBhIGNvcnJlbGF0aW9uIGJldHdlZW4gYWdlIGFuZCBsdW5nIGNhbmNlciBkaWFnbm9zaXMuJ30KZ2dwbG90KGRhdGEsIGFlcyh4ID0gTFVOR19DQU5DRVIsIHkgPSBBR0UpKSArCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSAic2t5Ymx1ZSIpICsKICBsYWJzKHggPSAiQ2F0ZWdvcnkiLCB5ID0gIlZhbHVlIiwgdGl0bGUgPSAiTHVuZyBDYW5jZXIgYnkgQWdlIikKCgoKCmBgYAoKIyMgQ0FSVCBDbGFzc2lmaWNhdGlvbgoKVGhlIG91dGNvbWUgdmFyaWFibGUgaXMgYSBiaW5hcnkgdmFyaWFibGUsIHNvIHRoaXMgYW5hbHlzaXMgd2lsbCByZWx5IG9uIENBUlQgQ2xhc3NpZmljYXRpb24gdG8gaGVscCBwcmVkaWN0IHRoZSBjYXRlZ29yaWNhbCB0YXJnZXQgdmFyaWFibGUuIApgYGB7cn0KCgoKCgojIFNwbGl0IGRhdGEgaW50byB0cmFpbmluZyAoNzAlKSBhbmQgdGVzdCAoMzAlKSBzZXRzCnNldC5zZWVkKDEyMykKdHJhaW4uaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRhJExVTkdfQ0FOQ0VSLCBwID0gMC43LCBsaXN0ID0gRkFMU0UpCnRyYWluLmRhdGEgPC0gZGF0YVt0cmFpbi5pbmRleCwgXQp0ZXN0LmRhdGEgPC0gZGF0YVstdHJhaW4uaW5kZXgsIF0KCiMgQnVpbGQgdGhlIGluaXRpYWwgY2xhc3NpZmljYXRpb24gdHJlZQp0cmVlLm1vZGVsIDwtIHJwYXJ0KExVTkdfQ0FOQ0VSIH4gLiwgCiAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmRhdGEsCiAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gImNsYXNzIiwgICAjIGNsYXNzaWZpY2F0aW9uIHRyZWUKICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiZ2luaSIsICAjIFVzaW5nIEdpbmkgaW5kZXgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI0ZOY29zdCA9IDEsIEZQY29zdCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9zcyA9IG1hdHJpeChjKDAsIDAuNSwgMSwgMCksIG5yb3cgPSAyKSAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2wobWluc3BsaXQgPSAxNSwgICMgTWluIDE1IG9icyB0byBzcGxpdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gNSwgICAjIE1pbiA3IG9icyBpbiBsZWFmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIENvbXBsZXhpdHkgcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcCA9IDAuMDAxLCAjIGNvbXBsZXggcGFyYW1ldGVyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDUpKSMgTWF4IHRyZWUgZGVwdGgKCgoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTcsIGZpZy5jYXA9J1RoZSB0cmVlIGNsYXNzaWZpY2F0aW9uIHNob3dzIHRoZSBmaXJzdCBzcGxpdCBhdCBzd2FsbG93aW5nIGRpZmZpY3VsdHksIGluZGljYXRpbmcgaXQgd2FzIHRoZSBtb3N0IHNpZ25pZmljYW50IHZhcmlhYmxlIG9mIHRoZSBzcGxpdC4gT3ZlcmFsbCwgaXQgc2hvd3MgdGhhdCBpbmRpdmlkdWFscyB3aXRoIGRpZmZpY3VsdHkgc3dhbGxvd2luZyBhbmQgYWxsZXJneSBoYXZlIGEgaGlnaCBjaGFuY2Ugb2YgZ2V0dGluZyBsdW5nIGNhbmNlci4gSXQgYWxzbyBzdWdnZXN0cyB0aGF0IGV2ZW4gd2l0aG91dCBhbGxlcmd5LCB3aGVlemluZywgYWxjb2hvbCBjb25zdW1wdGlvbiwgYW5kIHllbGxvdyBmaW5nZXJzIGNhbiByYWlzZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIGRpYWdub3NlZCB3aXRoIGx1bmcgY2FuY2VyLid9CnJwYXJ0LnBsb3QodHJlZS5tb2RlbCwgCiAgICAgICAgICAgZXh0cmEgPSAxMDQsICMgY2hlY2sgdGhlIGhlbHAgZG9jdW1lbnQgZm9yIG1vcmUgaW5mb3JtYXRpb24KICAgICAgICAgICAjIGNvbG9yIHBhbGV0dGUgaXMgYSBzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSB0aGF0IGJsZW5kcyBncmVlbiAoRykgdG8gYmx1ZSAoQnUpCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFKQoKCmBgYAoKYGBge3IgfQojIFByaW50IHRoZSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZQpwYW5kZXIodHJlZS5tb2RlbCRjcHRhYmxlKQoKYGBgCgpUaGUgdGFibGUgYWJvdmUgaXMgYSBjb21wbGV4aXR5IHBhcmFtZXRlciB0YWJsZSBmcm9tIHRoZSBkZWNpc2lvbiB0cmVlIGFib3ZlLiBBdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSB0cmVlIHRoZXJlIGFyZSB6ZXJvIHNwbGl0cyB3aXRoIGEgdHJhaW5pbmcgZXJyb3Igb2YgMCwgd2hpY2ggaXMgb3VyIGJhc2VsaW5lLiBBcyB0aGUgdHJlZSBncm93cyB0aGVyZSBhcmUgbW9yZSBzcGxpdHMgYW5kIGEgZGVjcmVhc2VkIHJlbGF0aXZlIGVycm9yLCBzdWdnZXN0aW5nIGEgYmV0dGVyIGZpdCBvbiB0cmFpbmluZyBkYXRhLiBJZGVhbGx5LCB3ZSB3YW50IHRoZSBzbWFsbGVzdCBzdWJ0cmVlIHdoZXJlIHhlcnJvciBpcyB3aXRoaW4gMSBTRSBvZiB0aGUgbWluaW11bSB4ZXJyb3IuIFRoZSBtaW5pbXVtIHhlcnJvciBpcyAwLjY3ODYsIHNob3duIGluIHRoZSBsYXN0IHJvdy4gVXNpbmcgdGhlIDEgU0UgcnVsZSwgb3VyIHRocmVzaG9sZCB3b3VsZCBiZSB0aGUgbWluaW11bSB4ZXJyb3IgcGx1cyBpdCdzIHN0YW5kYXJkIGRldmlhdGlvbiB3aGljaCB3b3VsZCBiZSAwLjgxMTQuIFRoZSBmaXJzdCByb3cgdG8gbWVldCB0aGlzIHJ1bGUgaXMgdGhlIHRoaXJkIHJvdyB3aGljaCBoYXMgNCBzcGxpdHMgYW5kIGEgQ1Agb2YgMC4wNTM1NywgYmVjYXVzZSBpdCdzIHhlcnJvciBpcyAwLjczMjEgd2hpY2ggaXMgbGVzcyB0aGFuIDAuODExNC5UaGlzIG1lYW5zIHRoZSB0cmVlIHNob3VsZCBiZSBwcnVuZWQgdG8gNCBzcGxpdHMgZm9yIG1vZGVsIHNpbXBsaWNpdHkgYW5kIGFjY3VyYWN5LgoKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhlIHBsb3QgYWJvdmUgc2hvd3MgdGhlIGNvcnNzLXZhbGlkYXJpb24gZXJyb3IgKHJlbCBlcnJvcikgdnMgdGhlIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgZnJvbSB0aGUgZGVjaXNpb24gdHJlZSBtb2RlbC4gVGhlIGhvcnJpem9udGFsIGRhc2hlZCBsaW5lIHNob3dzIHRoZSBtaW5pbXVtIGNyb3NzLXZhbGlkYXRpb24gZXJyb3IgKyAxU0UuIFRoZSAxLVNFIHJ1bGUgc3VnZ2VzdHMgY2hvb3NpbmcgdGhlIHNpbXBsZXN0IG1vZGVsIHdpdGhpbiAxIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBsb3dlc3QgY3Jvc3MtdmFsaWRhdGlvbiBlcnJvci4gVGhlIGRhc2hlZCBsaW5lIGNyb3NzZXMgdGhlIGVycm9yIGJhciBvZiB0aGUgdHJlZSBzaXplIDEsIG1lYW5pbmcgdGhpcyBpcyB0aGUgc2ltcGxlc3QgbW9kZWwgdGhhdCB3aWxsIHN0aWxsIHBlcmZvcm0gYWRlcXVhdGVseS4gT3VyIG9wdGltYWwgY3Agd291bGQgYmUgMC4wMDczLCB3aGljaCBpcyBhdCA2IHNwbGl0cy4nfQojIFBsb3QgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cwpwbG90Y3AodHJlZS5tb2RlbCkKCmBgYAoKIyMgUHJ1bmluZwoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD03LCBmaWcuY2FwPSdUaGUgYWJvdmUgbW9kZWwgc2hvd3MgdGhlIHRyZWUgYWZ0ZXIgcHJ1bmluZy4gVGhpcyByZXN1bHQgaXMgdGhlIHNhbWUgYXMgb3VyIGZpcnN0IG1vZGVsLCBzaW5jZSB0aGUgb3B0aW1hbCBjcCBpcyBhcyA2IHNwbGl0cy4nfQojIEZpbmQgdGhlIG9wdGltYWwgY3AgdmFsdWUgdGhhdCBtaW5pbWl6ZXMgY3Jvc3MtdmFsaWRhdGVkIGVycm9yCm1pbi5jcCA8LSB0cmVlLm1vZGVsJGNwdGFibGVbd2hpY2gubWluKHRyZWUubW9kZWwkY3B0YWJsZVssInhlcnJvciJdKSwiQ1AiXQoKIyBQcnVuZSB0aGUgdHJlZSB1c2luZyB0aGUgb3B0aW1hbCBjcApwcnVuZWQudHJlZS4xU0UgPC0gcHJ1bmUodHJlZS5tb2RlbCwgY3AgPSAwLjAwNzMpICAKcHJ1bmVkLnRyZWUubWluIDwtIHBydW5lKHRyZWUubW9kZWwsIGNwID0gbWluLmNwKQoKIyBWaXN1YWxpemUgdGhlIHBydW5lZCB0cmVlCnJwYXJ0LnBsb3QocHJ1bmVkLnRyZWUuMVNFLCAKICAgICAgICAgICBleHRyYSA9IDEwNCwgIyBjaGVjayB0aGUgaGVscCBkb2N1bWVudCBmb3IgbW9yZSBpbmZvcm1hdGlvbgogICAgICAgICAgICMgY29sb3IgcGFsZXR0ZSBpcyBhIHNlcXVlbnRpYWwgY29sb3Igc2NoZW1lIHRoYXQgYmxlbmRzIGdyZWVuIChHKSB0byBibHVlIChCdSkKICAgICAgICAgICBib3gucGFsZXR0ZSA9ICJHbkJ1IiwgIAogICAgICAgICAgIGJyYW5jaC5sdHkgPSAxLCAKICAgICAgICAgICBzaGFkb3cuY29sID0gImdyYXkiLCAKICAgICAgICAgICBubiA9IFRSVUUpCgpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NSwgZmlnLmNhcD0nVGhpcyBpcyB0aGUgZmluYWwgcHJ1bmVkIHRyZWUgYmFzZWQgb24gdGhlIHBsb3Qgb2YgY3Jvc3MtdmFsaWRhdGlvbiBlcnJvciB2cyBDUC4gVGhpcyBzaG93cyB0aGF0IHN3YWxsb3dpbmcgZGlmZmljdWx0eSBpcyBzaWduaWZpY2FudCBhbG9uZSBpbiBwcmVkaWN0aW5nIGx1bmcgY2FuY2VyLid9CiMgVmlzdWFsaXplIHRoZSBwcnVuZWQgdHJlZQpycGFydC5wbG90KHBydW5lZC50cmVlLm1pbiwgCiAgICAgICAgICAgZXh0cmEgPSAxMDQsICMgY2hlY2sgdGhlIGhlbHAgZG9jdW1lbnQgZm9yIG1vcmUgaW5mb3JtYXRpb24KICAgICAgICAgICAjIGNvbG9yIHBhbGV0dGUgaXMgYSBzZXF1ZW50aWFsIGNvbG9yIHNjaGVtZSB0aGF0IGJsZW5kcyBncmVlbiAoRykgdG8gYmx1ZSAoQnUpCiAgICAgICAgICAgYm94LnBhbGV0dGUgPSAiR25CdSIsICAKICAgICAgICAgICBicmFuY2gubHR5ID0gMSwgCiAgICAgICAgICAgc2hhZG93LmNvbCA9ICJncmF5IiwgCiAgICAgICAgICAgbm4gPSBUUlVFKQoKYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTUsIGZpZy5jYXA9J1RoZSBwbG90IGNvbXBhcmVzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIHRoZSBwcnVuZWQgVHJlZSBtb2RlbHMuIEJhc2VkIG9uIHRoZSBBVUMgYW5kIFJPQywgbG9naXN0aWMgcmVncmVzc2lvbiBwZXJmb3JtcyBiZXR0ZXIgdGhhbiB0aGUgb3RoZXIgdHdvIG1vZGVscy4nfQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3Qgc2V0CnRlc3QuZGF0YSRMVU5HX0NBTkNFUjwtIGFzLmZhY3Rvcih0ZXN0LmRhdGEkTFVOR19DQU5DRVIpCnRyYWluLmRhdGEkTFVOR19DQU5DRVI8LSBhcy5mYWN0b3IodHJhaW4uZGF0YSRMVU5HX0NBTkNFUikKCnByZWQubGFiZWwuMVNFIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUuMVNFLCB0ZXN0LmRhdGEsIHR5cGUgPSAiY2xhc3MiKSAjIGRlZmF1bHQgY3V0b2ZmIDAuNQpwcmVkLnByb2IuMVNFIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUuMVNFLCB0ZXN0LmRhdGEsIHR5cGUgPSAicHJvYiIpWywyXQojIwpwcmVkLmxhYmVsLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgdGVzdC5kYXRhLCB0eXBlID0gImNsYXNzIikgIyBkZWZhdWx0IGN1dG9mZiAwLjUKcHJlZC5wcm9iLm1pbiA8LSBwcmVkaWN0KHBydW5lZC50cmVlLm1pbiwgdGVzdC5kYXRhLCB0eXBlID0gInByb2IiKVssMl0KCiMgQ29uZnVzaW9uIG1hdHJpeAojY29uZi5tYXRyaXggPC0gY29uZnVzaW9uTWF0cml4KHByZWQubGFiZWwsIHRlc3QuZGF0YSRkaWFiZXRlcywgcG9zaXRpdmUgPSAicG9zIikKI3ByaW50KGNvbmYubWF0cml4KQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyAgbG9naXN0aWMgcmVncmVzc2lvbgpsb2dpdC5maXQgPC0gZ2xtKExVTkdfQ0FOQ0VSIH4gLiwgZGF0YSA9IHRyYWluLmRhdGEsIGZhbWlseSA9IGJpbm9taWFsKQpBSUMubG9naXQgPC0gc3RlcChsb2dpdC5maXQsIGRpcmVjdGlvbiA9ICJib3RoIiwgdHJhY2UgPSAwKQpwcmVkLmxvZ2l0IDwtIHByZWRpY3QoQUlDLmxvZ2l0LCB0ZXN0LmRhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBST0MgY3VydmUgYW5kIEFVQwpyb2MudHJlZS4xU0UgPC0gcm9jKHRlc3QuZGF0YSRMVU5HX0NBTkNFUiwgcHJlZC5wcm9iLjFTRSkKcm9jLnRyZWUubWluIDwtIHJvYyh0ZXN0LmRhdGEkTFVOR19DQU5DRVIsIHByZWQucHJvYi5taW4pCnJvYy5sb2dpdCA8LSByb2ModGVzdC5kYXRhJExVTkdfQ0FOQ0VSLCBwcmVkLmxvZ2l0KQoKIyMKIyMjIFNlbi1TcGUKdHJlZS4xU0Uuc2VuIDwtIHJvYy50cmVlLjFTRSRzZW5zaXRpdml0aWVzCnRyZWUuMVNFLnNwZSA8LSByb2MudHJlZS4xU0Ukc3BlY2lmaWNpdGllcwojCnRyZWUubWluLnNlbiA8LSByb2MudHJlZS5taW4kc2Vuc2l0aXZpdGllcwp0cmVlLm1pbi5zcGUgPC0gcm9jLnRyZWUubWluJHNwZWNpZmljaXRpZXMKIwpsb2dpdC5zZW4gPC0gcm9jLmxvZ2l0JHNlbnNpdGl2aXRpZXMKbG9naXQuc3BlIDwtIHJvYy5sb2dpdCRzcGVjaWZpY2l0aWVzCiMjIEFVQwphdWMudHJlZS4xU0UgPC0gcm9jLnRyZWUuMVNFJGF1YwphdWMudHJlZS5taW4gPC0gcm9jLnRyZWUubWluJGF1YwphdWMubG9naXQgPC0gcm9jLmxvZ2l0JGF1YwojIyMKcGxvdCgxLWxvZ2l0LnNwZSwgbG9naXQuc2VuLCAgCiAgICAgeGxhYiA9ICIxIC0gc3BlY2lmaWNpdHkiLAogICAgIHlsYWIgPSAic2Vuc2l0aXZpdHkiLAogICAgIGNvbCA9ICJkYXJrcmVkIiwKICAgICB0eXBlID0gImwiLAogICAgIGx0eSA9IDEsCiAgICAgbHdkID0gMSwKICAgICBtYWluID0gIlJPQzogQ0FSVCBhbmQgTG9naXN0aWMgUmVncmVzc2lvbiIpCmxpbmVzKDEtdHJlZS4xU0Uuc3BlLCB0cmVlLjFTRS5zZW4sIAogICAgICBjb2wgPSAiYmx1ZSIsCiAgICAgIGx0eSA9IDEsCiAgICAgIGx3ZCA9IDEpCmxpbmVzKDEtdHJlZS5taW4uc3BlLCB0cmVlLm1pbi5zZW4sICAgICAgCiAgICAgIGNvbCA9ICJvcmFuZ2UiLAogICAgICBsdHkgPSAxLAogICAgICBsd2QgPSAxKQphYmxpbmUoMCwxLCBjb2wgPSAic2t5Ymx1ZTMiLCBsdHkgPSAyLCBsd2QgPSAyKQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygiTG9naXN0aWMiLCAiVHJlZSAxU0UiLCAiVHJlZSBNaW4iKSwKICAgICAgIGx0eSA9IGMoMSwxLDEpLCBsd2QgPSByZXAoMSwzKSwKICAgICAgIGNvbCA9IGMoInJlZCIsICJibHVlIiwgIm9yYW5nZSIpLAogICAgICAgYnR5PSJuIixjZXggPSAwLjgpCiMjIGFubm90YXRpb24gLSBBVUMKdGV4dCgwLjgsIDAuNDYsIHBhc3RlKCJMb2dpc3RpYyBBVUM6ICIsIHJvdW5kKGF1Yy5sb2dpdCw0KSksIGNleCA9IDAuOCkKdGV4dCgwLjgsIDAuNCwgcGFzdGUoIlRyZWUgMVNFIEFVQzogIiwgcm91bmQoYXVjLnRyZWUuMVNFLDQpKSwgY2V4ID0gMC44KQp0ZXh0KDAuOCwgMC4zNCwgcGFzdGUoIlRyZWUgTWluIEFVQzogIiwgcm91bmQoYXVjLnRyZWUubWluLDQpKSwgY2V4ID0gMC44KQoKYGBgCgojIyBPcHRpbWFsIEN1dG9mZgoKYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01LCBmaWcuY2FwPSdUaGUgYWJvdmUgcGxvdCBzaG93cyB0aGUgb3B0aW1hbCBjdXRvZmYgdG8gYmUgMC4zODg5LiBUaGlzIHRocmVzaG9sZCBpcyB1c2VkIHRvIG1pbmltaXplIG1pc3NjbGFzc2lmaWNhdGlvbi4nfQojIHByZWRpdGl2ZSBwcm9iYWJpbGl0aWVzIG9mIHRyZWUubWluIG1vZGVsLgpwcmVkLnByb2IubWluIDwtIHByZWRpY3QocHJ1bmVkLnRyZWUubWluLCB0cmFpbi5kYXRhLCB0eXBlID0gInByb2IiKVssMl0KIyMKY29zdCA8LSBOVUxMCmN1dG9mZiA8LXNlcSgwLDEsIGxlbmd0aCA9IDEwKQojIwpmb3IgKGkgaW4gMToxMCl7CiAgcHJlZC5sYWJlbCA8LSBpZmVsc2UocHJlZC5wcm9iLm1pbiA+IGN1dG9mZltpXSwgIllFUyIsICJOTyIpCiAgRk4gPC0gc3VtKHByZWQubGFiZWwgPT0gIk5PIiAmIHRyYWluLmRhdGEkTFVOR19DQU5DRVIgPT0gIllFUyIpCiAgRlAgPC0gc3VtKHByZWQubGFiZWwgPT0gIllFUyIgJiB0cmFpbi5kYXRhJExVTkdfQ0FOQ0VSID09ICJOTyIpCiAgY29zdFtpXSA9IDUqRlAgKyAyMCpGTgp9CiMjIGlkZW50aWZ5IG9wdGltYWwgY3V0LW9mZgptaW4uSUQgPC0gd2hpY2goY29zdCA9PSBtaW4oY29zdCkpICAgIyBjb3VsZCBoYXZlIG11bHRpcGxlIG1pbmltdW0Kb3B0aW0ucHJvYiA8LSBtZWFuKGN1dG9mZlttaW4uSURdKSAgICMgdGFrZSB0aGUgYXZlcmFnZSBvZiB0aGUgY3V0LW9mZnMKIyMKcGxvdChjdXRvZmYsIGNvc3QsIHR5cGUgPSAiYiIsIGNvbCA9ICJuYXZ5IiwKICAgICBtYWluID0gIkN1dG9mZiB2cyBNaXNjbGFzc2lmaWNhdGlvbiBDb3N0IikKIyMKdGV4dCgwLjIsIDM1MDAsIHBhc3RlKCJPcHRpbWFsIGN1dG9mZjoiLCByb3VuZChvcHRpbS5wcm9iLDQpKSwgY2V4ID0gMC44KQoKYGBgCgojIyBDb25jbHVzaW9uCgpUaGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBwZXJmb3JtcyBzdXBlcmlvciB0byB0aGUgc3VnZ2VzdGVkIENBUlQgQ2xhc3NpZmljYXRpb24gbW9kZWxzLiBIb3dldmVyLCB0aGUgQ0FSVCBtb2RlbHMgc2hvd2VkIHRoYXQgZGlmZmljdWx0eSBicmVhdGhpbmcgd2FzIGEga2V5IHByZWRpY3RvciBpbiBsdW5nIGNhbmNlci4gT3RoZXIga2V5IHByZWRpY3RvcnMgd2VyZSBhbGNvaG9sIGNvbnN1bXB0aW9uLCB3aGVlemluZywgYW5kIGFsbGVyZ3kuIA==